// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: dom_test.cpp 316 2009-01-08 18:01:10Z heavyzheng $
//

#include <zzheng/xml/dom_test.hpp>
#include <zzheng/test_logging.hpp>
#include <zzheng/test_resource.hpp>
#include <zzheng/test_utility.hpp>

#include <zzheng/xml/dom.hpp>
#include <zzheng/xml/dom/utility.hpp>
#include <zzheng/utility/sys_env.hpp>

#ifdef ZZHENG_MSVC
#  pragma warning(push)
#  pragma warning(disable: 4127 4511 4512 4701)
#endif // ZZHENG_MSVC

#include <boost/lexical_cast.hpp>

#ifdef ZZHENG_MSVC
#  pragma warning(pop)
#endif // ZZHENG_MSVC

#include <boost/shared_ptr.hpp>
#include <exception>
#include <string>
#include <list>
#include <iostream>
#include <fstream>

namespace zzheng {
namespace test {

    namespace dom = zzheng::xml::dom; // namespace alias.

    ////////////////////////////////////////////////////////////////////////////////////////////////

    namespace {

        void xml_to_records(const dom::element& elem,
                            int level,
                            std::list<std::string>& records) {

            // Create a CSV record for the element.
            csv_record record;
            record << level
                   << elem.name()
                   << elem.uri()
                   << elem.attributes().size()
                   << elem.children().size();
            if (elem.children().size() == 1U && elem.children().begin()->type() == dom::text_node) {
                record << elem.content();
            } else {
                record << std::string();
            }
            records.push_back(record.str());

            // Create a CSV record for each attribute attached to this element.
            const dom::attribute_map& attrs = elem.attributes();
            for (dom::const_attribute_iterator i = attrs.begin(); i != attrs.end(); ++i) {
                record.clear();
                record << level
                       << ("@" + i->name())
                       << i->uri()
                       << std::string()
                       << std::string()
                       << i->value();
                records.push_back(record.str());
            }

            // Create CSV records recursively for the child elements.
            const dom::child_node_list& children = elem.children();
            for (dom::const_child_iterator i = children.begin(); i != children.end(); ++i) {
                if (i->type() == dom::element_node) {
                    xml_to_records( *dom::dynamic_node_cast<const dom::element>(i.ptr()),
                                    level + 1,
                                    records );
                }
            }
        }

        void read_lines(const std::string& file_name, std::list<std::string>& list) {
            std::ifstream fin(file_name.c_str(), std::ios::in);
            if (fin) {
                const int BUFFER_SIZE = 1024;
                char buffer[BUFFER_SIZE];
                while (fin.getline(buffer, BUFFER_SIZE)) {
                    std::string line = buffer;
                    if (!line.empty()) {
                        list.push_back(line);
                    }
                }
                fin.close();
            }
        }

        void check_with_csv(const dom::element& root, const std::string& csv_file_name) {
            
            std::list<std::string> xml_records;
            xml_to_records(root, 1, xml_records);
            BOOST_CHECK(!xml_records.empty());

            std::list<std::string> csv_records;
            read_lines(csv_file_name, csv_records);
            BOOST_CHECK(!csv_records.empty());

            BOOST_CHECK(xml_records.size() == csv_records.size());
            typedef std::list<std::string>::iterator iterator;
            for (iterator i = xml_records.begin(); i != xml_records.end(); ++i) {
                BOOST_CHECK(!csv_records.empty());
                if (!csv_records.empty()) {
                    BOOST_CHECK_EQUAL(*i, csv_records.front());
                    csv_records.pop_front();
                }
            }
            BOOST_CHECK(csv_records.empty());
        }

    } // anonymous namespace

    ////////////////////////////////////////////////////////////////////////////////////////////////

    void test_parse_numbers() {

        info("Running test case: test_parse_numbers");

        std::string xml_path = zzheng::sys::join_path(get_resource_dir(), "xml", "numbers.xml");
        dom::parser parser;
        boost::shared_ptr<dom::document> doc(parser.parse_file(xml_path));
        BOOST_CHECK(doc != 0 && doc->root() != 0);

        std::string csv_path = zzheng::sys::join_path(get_resource_dir(), "xml", "numbers.csv");
        check_with_csv(*(doc->root()), csv_path);
    }

    void test_create_numbers() {

        info("Running test case: test_create_numbers");

        // Parity (odd or even) namespace URI and prefix.
        const std::string PARITY_URI = "http://en.wikipedia.org/wiki/Parity_(mathematics)";

        // Create root element.
        dom::document doc;
        dom::element_ptr numbers = doc.reset_root(dom::make_element("numbers"));
        BOOST_CHECK(numbers != 0);
        numbers->declare_namespace(PARITY_URI);
        numbers->attributes().insert(dom::make_attribute("min",  "1"));
        numbers->attributes().insert(dom::make_attribute("max", "10"));

        // Create child elements.
        for (int i = 1; i <= 10; ++i) {

            std::string curr_value = boost::lexical_cast<std::string>(i);
            std::string prev_value = boost::lexical_cast<std::string>(i - 1);
            std::string next_value = boost::lexical_cast<std::string>(i + 1);

            // Create "number" element.
            dom::element_ptr number = numbers->children().push_back(dom::make_element("number"));
            BOOST_CHECK(number != 0);
            number->attributes().insert(dom::make_attribute("value", curr_value));

            // Create "number/prev" element.
            dom::element_ptr prev = number->children().push_back(dom::make_element("prev"));
            BOOST_CHECK(prev != 0);
            prev->children().push_back(dom::make_text(prev_value));

            // Create "number/next" element.
            dom::element_ptr next = number->children().push_back(dom::make_element("next"));
            BOOST_CHECK(next != 0);
            next->children().push_back(dom::make_text(next_value));

            // Create "number/p:even" or "number/p:odd" element.
            if (i % 2 == 0) {
                number->children().push_back(dom::make_element(PARITY_URI, "even"));
            } else {
                number->children().push_back(dom::make_element(PARITY_URI, "odd"));
            }
        }

        std::string csv_path = zzheng::sys::join_path(get_resource_dir(), "xml", "numbers.csv");
        check_with_csv(*(doc.root()), csv_path);
        // std::cout << doc.str() << std::endl;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    void add_dom_test_cases(boost::unit_test::test_suite& suite) {
        suite.add( BOOST_TEST_CASE(&test_parse_numbers) );
        suite.add( BOOST_TEST_CASE(&test_create_numbers) );
    }


} // namespace zzheng::test
} // namespace zzheng





